package com.ruoyi.workflow.service.impl;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.ruoyi.business.domain.ServiceItem;
import com.ruoyi.business.service.IServiceItemService;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.workflow.domain.BusBpmnInfo;
import com.ruoyi.workflow.service.IBpmnNodeService;
import com.ruoyi.workflow.service.IBusBpmnInfoService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.workflow.mapper.CarPackageAuditMapper;
import com.ruoyi.workflow.domain.CarPackageAudit;
import com.ruoyi.workflow.service.ICarPackageAuditService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 套餐信息Service业务层处理
 *
 * @author yeqi
 * @date 2022-11-18
 */
@Service
public class CarPackageAuditServiceImpl extends ServiceImpl<CarPackageAuditMapper, CarPackageAudit> implements ICarPackageAuditService {

    @Autowired
    private IServiceItemService serviceItemService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

   @Autowired
   private IBpmnNodeService bpmnNodeService;

   @Autowired
   private RepositoryService repositoryService;



   @Autowired
   private ProcessDiagramGenerator processDiagramGenerator;
    /**
     * 查询套餐信息列表
     *
     * @param carPackageAudit 套餐信息
     * @return 套餐信息
     */
    @Override
    public List<CarPackageAudit> selectCarPackageAuditList(CarPackageAudit carPackageAudit) {
        return getBaseMapper().selectCarPackageAuditList(carPackageAudit);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void audit(Long serviceItemId,String info) {
        // 1. 根据服务项 id 查询服务项对象
        ServiceItem serviceItem = serviceItemService.get(serviceItemId);
        // 2. 校验审核合法性(是套餐 && 状态为初始化), 提示当前无法审核
        if (ServiceItem.CARPACKAGE_NO.equals(serviceItem.getCarPackage()) ){
            throw new ServiceException("不是套餐无序审核");
        }
        if ( !ServiceItem.AUDITSTATUS_INIT.equals(serviceItem.getSaleStatus())){
            throw new ServiceException("只有初始化状态才能审核");
        }
        // 3. 修改服务项状态为审核中
        serviceItemService.changeStatus(serviceItemId,ServiceItem.AUDITSTATUS_AUDITING);
        // 4. 创建一条审核记录信息, 设置相关数据
        CarPackageAudit audit = new CarPackageAudit();
        // 4.1 将服务项信息转换为 json 字符串设置到审核记录中
        audit.setServiceItemId(serviceItemId);
        audit.setServiceItemInfo(serviceItem.getInfo());
        audit.setServiceItemName(serviceItem.getName());
        audit.setServiceItemPrice(serviceItem.getDiscountPrice());
        // 4.2 获取到当前登录用户, 将其 id 设置到审核记录中
        audit.setCreatorId(SecurityUtils.getUserId() + "");
        audit.setCreatorName(SecurityUtils.getUsername());
        // 4.3 将审核状态设置为进行中
        audit.setStatus(CarPackageAudit.PACKAGE_AUDIT_STATUS_PENDING);
        audit.setCreateTime(new Date());
        audit.setInfo(info);
        // 4.4 保存审核记录信息
        super.save(audit);
        // 5. 构建流程变量(BusinessKey/金额/审核人员)
        Map<String,Object> map = new HashMap<>();
        // 5.1 BusinessKey = 审核记录的 id
        String  businessKey = audit.getId() + "";
        // 5.2 金额 = 服务项的折扣金额
        map.put("money",audit.getServiceItemPrice().longValue() + "");
        // 5.3 审核人员 = 查询当前流程实例对应的节点列表, 获取到每一个节点的人员列表
        // 6. 基于流程定义 Key 开启流程实例, 得到流程实例对象
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(
                CarPackageAudit.CAR_PACKAGE_DEFINITION_KEY,
                businessKey,
                map
        );

        // 6.1 将流程实例 id 设置到审核记录中
        audit.setInstanceId(instance.getId());
        // 6.2 更新审核记录表
        super.updateById(audit);
        // 7. 为任务添加候选人
        Task task = taskService.createTaskQuery()
                .processInstanceId(instance.getId())
                .singleResult();
       List<SysUser> userList =   bpmnNodeService.selectUserByKey(task.getTaskDefinitionKey());
        for (SysUser user : userList) {
            taskService.addCandidateUser(task.getId(),user.getUserName());
        }


    }

    @Override
    public InputStream getProcessingImage(String instanceId) {
        // 基于流程实例ID查询流程实例对象
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(instanceId)
                .singleResult();
        if (instance == null){
            throw new ServiceException("非法操作");
        }
        // 根据流程定义对象拿到bpmnModel对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
        // 获取历史活动,用于图片高亮
        List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(instanceId)
                .list();
        List<String> activtyIdList = activityInstanceList.stream().map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());
        // 调用画图API

        return processDiagramGenerator.generateDiagram(bpmnModel,activtyIdList, Collections.emptyList(),"宋体","宋体","宋体");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void repeal(Long id, String reason) {
        // 1. 基于审核记录id查询审核记录对象
        CarPackageAudit audit = getById(id);
        // 2. 基于审核记录中的服务项 id, 将服务项状态修改为初始化
        if (audit==null){
            throw new ServiceException("非法操作");
        }
        if (!CarPackageAudit.PACKAGE_AUDIT_STATUS_PENDING.equals(audit.getStatus())){
            throw new ServiceException("无法撤销");
        }
        serviceItemService.changeStatus(audit.getServiceItemId(),ServiceItem.AUDITSTATUS_INIT);
        // 3. 将审核记录的状态修改为撤销申请
        audit.setStatus(CarPackageAudit.PACKAGE_AUDIT_STATUS_CANCELED);
        updateById(audit);
        // 4. 调用 activiti 的 api 删除流程实例, 提供删除理由
        runtimeService.deleteProcessInstance(audit.getInstanceId(),reason);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doAudit(Long id, String taskId, boolean result, String info) {
        // 1. 根据审核记录 id 获取审核记录对象, 并判断状态为审核中才允许审核
        CarPackageAudit carPackageAudit = getById(id);
        if (!CarPackageAudit.PACKAGE_AUDIT_STATUS_PENDING.equals(carPackageAudit.getStatus())){
            throw new ServiceException("非法操作");
        }
        // 2. 基于当前用户名作为候选人/负责人条件查询任务信息, 如果查询不到提示非法操作
        String username = SecurityUtils.getUsername();
        Task task = taskService.createTaskQuery()
                .taskCandidateOrAssigned(username)
                .taskId(taskId)
                .singleResult();
        if (task == null){
            throw new ServiceException("非法操作,任务不存在");
        }
        // 3. 设置流程变量(审核结果)
        taskService.setVariable(taskId,task.getTaskDefinitionKey(),result);
        // 4. 领取任务/完成任务/添加批注
        taskService.claim(taskId,username);
        taskService.addComment(taskId,task.getProcessInstanceId(),info);
        taskService.complete(taskId);
        // 5. 基于流程实例查询下一个进行中的任务, 根据是否有下一个任务判断流程是否结束
        Task nextTask = taskService.createTaskQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        if (nextTask != null){
            // 6. 有下一个任务
            // 6.1. 判断当前审核结果是通过还是拒绝
            if (result){
                // 6.2. 如果是审核通过, 基于任务 key 查询候选人列表
                List<SysUser> users = bpmnNodeService.selectUserByKey(nextTask.getTaskDefinitionKey());
                // 6.2.1. 为下一个任务设置候选人
                for (SysUser user : users) {
                    taskService.addCandidateUser(nextTask.getId(),user.getUserName());
                }
            }else {
                // 6.3. 如果是审核拒绝, 为任务设置候选人为发起人
                taskService.addCandidateUser(nextTask.getId(),carPackageAudit.getCreatorName());
                // 6.3.1. 通过审核记录对象的创建 id, 查询创建人用户对象

                // 6.3.2. 将创建人对象的用户名设置到任务的候选人

                // 6.3.3. 更新服务项的状态为重新调整
                serviceItemService.changeStatus(carPackageAudit.getServiceItemId(),ServiceItem.AUDITSTATUS_REPLY);

                // 6.3.4 更新审核记录状态
                carPackageAudit.setStatus(CarPackageAudit.PACKAGE_AUDIT_STATUS_REJECTED);
                this.updateById(carPackageAudit);
            }

        }else {
            // 7. 没有任务, 在我们的流程中代表结果一定为通过, 且流程结束
            // 7.1. 修改服务项状态为审核通过
            serviceItemService.changeStatus(carPackageAudit.getServiceItemId(),ServiceItem.AUDITSTATUS_APPROVED);
            // 7.2. 修改审核记录状态为审核通过
            carPackageAudit.setStatus(CarPackageAudit.PACKAGE_AUDIT_STATUS_PASSED);
            this.updateById(carPackageAudit);
        }


    }

    @Override
    public void updateServiceItem(Long auditId, ServiceItem serviceItem) {
        // 根据 id 更新审核对象中的服务项信息
        // UPDATE bus_car_package_audit
        // set service_item_name = ?, service_item_info = ?, service_item_price = ?
        // where id = ?
        super.update(new LambdaUpdateWrapper<CarPackageAudit>() // 创建更新条件构造对象, 记得要设置泛型
                .set(CarPackageAudit::getServiceItemName, serviceItem.getName()) // 指定修改 serviceItemName 属性
                .set(CarPackageAudit::getServiceItemInfo, serviceItem.getInfo()) // 指定修改 serviceItemInfo 属性
                .set(CarPackageAudit::getServiceItemPrice, serviceItem.getDiscountPrice()) // 指定修改 serviceItemPrice 属性
                .eq(CarPackageAudit::getId, auditId)); // 修改条件为 id=auditId
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void reapply(Long id, String taskId) {
        // 1. 基于审核记录id查询审核记录对象
        CarPackageAudit audit = this.getById(id);
        // 2. 验证当前状态是否是审核拒绝, 如果不是审核拒绝, 直接抛出异常
        if (!CarPackageAudit.PACKAGE_AUDIT_STATUS_REJECTED.equals(audit.getStatus())){
            throw new ServiceException("非法操作");
        }
        String username = SecurityUtils.getUsername();
        // 3. 基于任务 id + 当前用户名查询任务对象, 确认是否是审核人
        Task task = taskService.createTaskQuery()
                .taskCandidateOrAssigned(username)
                .taskId(taskId)
                .singleResult();
        // 4. 创建流程变量对象, 重新设置 money 的值
        taskService.setVariable(taskId,"money",audit.getServiceItemPrice().longValue());
        
        // 5. 如果能查到, 领取并完成任务, 同时设置流程变量
        if (task == null){
            throw  new ServiceException("非法操作");
        }
        taskService.claim(taskId,username);
        taskService.complete(taskId);
        // 6. 查询下一个代办任务
        Task nextTask = taskService.createTaskQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        // 7. 基于该任务查询候选人列表
        List<SysUser> users = bpmnNodeService.selectUserByKey(nextTask.getTaskDefinitionKey());
        for (SysUser user : users) {
            // 8. 为该任务设置候选人
            taskService.addCandidateUser(nextTask.getId(),user.getUserName());
        }
        // 9. 重新将服务项/审核记录的状态修改为审核中
        serviceItemService.changeStatus(audit.getServiceItemId(),ServiceItem.AUDITSTATUS_AUDITING);
        audit.setStatus(CarPackageAudit.PACKAGE_AUDIT_STATUS_PENDING);
        this.updateById(audit);
    }


}
